/**
 * 
 */
package com.eagle.coders.core.dynamic.compiler;

import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

import javax.tools.Diagnostic;
import javax.tools.DiagnosticCollector;
import javax.tools.JavaCompiler;
import javax.tools.JavaCompiler.CompilationTask;
import javax.tools.JavaFileManager;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;

/**
 * @author Anees
 * 
 */
public class DynamicCompiler {

	private String fullName;
	private String sourceCode;

	// private JavaFileManager javaFileManager;
	// private DiagnosticCollector<JavaFileObject> diagnostics;

	private JavaCompiler compiler;
	private DiagnosticCollector<JavaFileObject> collector;
	private JavaFileManager manager;

	public DynamicCompiler(String fullName, String sourceCode) throws Exception{
		this.fullName = fullName;
		this.sourceCode = sourceCode;
		init();

		// diagnostics = new DiagnosticCollector<JavaFileObject>();

		// javaFileManager = initFileManager();
	}

	public void init() throws Exception {
		compiler = ToolProvider.getSystemJavaCompiler();
		collector = new DiagnosticCollector<JavaFileObject>();
		manager = new DynamicClassFileManager<JavaFileManager>(
				compiler.getStandardFileManager(null, null, null));
	}

	/**
	 * 
	 * @return
	 */
	public Class<?> compile() throws ClassNotFoundException, Exception {
		return compileToClass(fullName, sourceCode);
	}
	
	public Class<?> compileToClassOne(String fullName, String javaCode) throws Exception{
		System.out.println("FileName :: " + fullName);
		System.out.println("Source Code :\n" + sourceCode);

		JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();

		StandardJavaFileManager fileManager = compiler.getStandardFileManager(
				null, null, null);

		// Dynamic compiling requires specifying
		// a list of "files" to compile. In our case
		// this is a list containing one "file" which is in our case
		// our own implementation (see details below)
		List<JavaFileObject> jfiles = new ArrayList<JavaFileObject>();

		JavaFileObject javaFileObject = new DynamicJavaStringSourceObject(
				fullName, sourceCode);

		jfiles.add(javaFileObject);

		String[] opts = new String[] { "-d", "C:/eagleCoders/bin" };

		// CompilationTask task = compiler.getTask(null, fileManager, null,
		// Arrays.asList(opts), null, jfiles);
		CompilationTask task = compiler.getTask(null, fileManager, null,
				Arrays.asList(opts), null, jfiles);


		boolean success = task.call();

		if (success) {
			File classesDir = new File("C:/eagleCoders/bin");
			URL[] classpath = new URL[] { classesDir.toURI().toURL() };
			URLClassLoader urlClassloader = new URLClassLoader(classpath,
					DynamicCompiler.class.getClassLoader());
			Class<?> clazz = urlClassloader.loadClass(fullName);
			
			System.out.println("Clazz :: " + clazz.getCanonicalName());
			return clazz;
		} else {
			System.out.printf("***** Compilation failed!!!\n");
			return null;
		}
	}

	/**
	 * 
	 * @param fullName
	 * @param javaCode
	 * @return
	 * @throws Exception
	 */
	public Class<?> compileToClass(String fullName, String javaCode)
			throws Exception {
		Class<?> clazz = null;
		DynamicJavaStringSourceObject strFile = new DynamicJavaStringSourceObject(
				fullName, javaCode);
		Iterable<? extends JavaFileObject> units = Arrays.asList(strFile);
		CompilationTask task = compiler.getTask(null, manager, collector, null,
				null, units);
		boolean status = task.call();
		if (status) {
			System.out.printf("Compilation successful!!!\n");
			clazz = manager.getClassLoader(null).loadClass(fullName);
		} else {
			System.out.printf("Message:\n");
			for (Diagnostic<?> d : collector.getDiagnostics()) {
				System.out.printf("%s\n", d.getMessage(null));
			}
			System.out.printf("***** Compilation failed!!!\n");
		}
		System.out.println("ClassName :: "+ clazz.getCanonicalName());
		return clazz;
	}

}
